<?php

declare(strict_types=1);

namespace Core;

/**
 * Flash 消息类
 *
 * 用于在请求之间传递一次性状态信息（如成功、错误提示）。
 * 消息在被检索一次后会自动清除。
 * 需要会话支持。
 */
class Flash
{
    /** 会话中存储闪存消息的键名 */
    private const SESSION_KEY = '_flash_messages';

    /** 需要保持到下次请求的消息键名 */
    private const SESSION_KEEP_KEY = '_flash_keep';

    /** 预定义的消息级别 */
    public const LEVEL_SUCCESS = 'success';
    public const LEVEL_ERROR   = 'error';
    public const LEVEL_WARNING = 'warning';
    public const LEVEL_INFO    = 'info';

    /** 每个级别的最大消息数量（防止内存溢出） */
    private const MAX_MESSAGES_PER_LEVEL = 50;

    /**
     * 添加一条闪存消息
     *
     * @param string $level 消息级别
     * @param string $message 消息内容
     * @param bool $htmlSafe 是否转义 HTML（默认转义以防 XSS）
     * @return void
     */
    public static function add(string $level, string $message, bool $htmlSafe = true): void
    {
        self::ensureSessionStarted();

        if (!isset($_SESSION[self::SESSION_KEY])) {
            $_SESSION[self::SESSION_KEY] = [];
        }

        if (!isset($_SESSION[self::SESSION_KEY][$level])) {
            $_SESSION[self::SESSION_KEY][$level] = [];
        }

        // 防止消息过多占用内存
        if (count($_SESSION[self::SESSION_KEY][$level]) >= self::MAX_MESSAGES_PER_LEVEL) {
            array_shift($_SESSION[self::SESSION_KEY][$level]); // 移除最旧的消息
        }

        $_SESSION[self::SESSION_KEY][$level][] = [
            'message' => $htmlSafe ? htmlspecialchars($message, ENT_QUOTES, 'UTF-8') : $message,
            'timestamp' => time(),
        ];
    }

    /**
     * 添加一条 'success' 级别的闪存消息
     */
    public static function success(string $message, bool $htmlSafe = true): void
    {
        self::add(self::LEVEL_SUCCESS, $message, $htmlSafe);
    }

    /**
     * 添加一条 'error' 级别的闪存消息
     */
    public static function error(string $message, bool $htmlSafe = true): void
    {
        self::add(self::LEVEL_ERROR, $message, $htmlSafe);
    }

    /**
     * 添加一条 'warning' 级别的闪存消息
     */
    public static function warning(string $message, bool $htmlSafe = true): void
    {
        self::add(self::LEVEL_WARNING, $message, $htmlSafe);
    }

    /**
     * 添加一条 'info' 级别的闪存消息
     */
    public static function info(string $message, bool $htmlSafe = true): void
    {
        self::add(self::LEVEL_INFO, $message, $htmlSafe);
    }

    /**
     * 检索所有闪存消息，并立即清除它们
     *
     * @param bool $formatted 是否返回格式化的消息（仅包含消息文本）
     * @return array 所有闪存消息
     */
    public static function all(bool $formatted = true): array
    {
        self::ensureSessionStarted();
        self::ageFlashData(); // 处理需要保持的消息

        $messages = $_SESSION[self::SESSION_KEY] ?? [];
        unset($_SESSION[self::SESSION_KEY]);

        return $formatted ? self::formatMessages($messages) : $messages;
    }

    /**
     * 检查是否存在指定级别的闪存消息
     *
     * @param string|null $level 消息级别，null 检查任何消息
     * @return bool
     */
    public static function has(?string $level = null): bool
    {
        self::ensureSessionStarted();

        if ($level === null) {
            return !empty($_SESSION[self::SESSION_KEY]);
        }

        return !empty($_SESSION[self::SESSION_KEY][$level]);
    }

    /**
     * 获取指定级别的所有闪存消息，并立即清除该级别的消息
     *
     * @param string $level 消息级别
     * @param bool $formatted 是否返回格式化的消息
     * @return array 该级别的消息列表
     */
    public static function get(string $level, bool $formatted = true): array
    {
        self::ensureSessionStarted();

        $messages = $_SESSION[self::SESSION_KEY][$level] ?? [];
        unset($_SESSION[self::SESSION_KEY][$level]);

        // 清理空数组
        if (isset($_SESSION[self::SESSION_KEY]) && empty($_SESSION[self::SESSION_KEY])) {
            unset($_SESSION[self::SESSION_KEY]);
        }

        return $formatted ? self::formatMessages(['temp' => $messages])['temp'] ?? [] : $messages;
    }

    /**
     * 查看消息但不清除（用于预览）
     *
     * @param string|null $level 消息级别，null 返回所有
     * @param bool $formatted 是否返回格式化的消息
     * @return array
     */
    public static function peek(?string $level = null, bool $formatted = true): array
    {
        self::ensureSessionStarted();

        $messages = $_SESSION[self::SESSION_KEY] ?? [];

        if ($level !== null) {
            $messages = [$level => $messages[$level] ?? []];
        }

        return $formatted ? self::formatMessages($messages) : $messages;
    }

    /**
     * 保持当前所有闪存消息到下一个请求
     *
     * @param string|null $level 指定级别，null 保持所有
     * @return void
     */
    public static function keep(?string $level = null): void
    {
        self::ensureSessionStarted();

        if (!isset($_SESSION[self::SESSION_KEY])) {
            return;
        }

        $_SESSION[self::SESSION_KEEP_KEY] = $level ?? true;
    }

    /**
     * 清除所有或指定级别的闪存消息
     *
     * @param string|null $level 消息级别，null 清除所有
     * @return void
     */
    public static function clear(?string $level = null): void
    {
        self::ensureSessionStarted();

        if ($level === null) {
            unset($_SESSION[self::SESSION_KEY]);
        } elseif (isset($_SESSION[self::SESSION_KEY][$level])) {
            unset($_SESSION[self::SESSION_KEY][$level]);

            if (empty($_SESSION[self::SESSION_KEY])) {
                unset($_SESSION[self::SESSION_KEY]);
            }
        }
    }

    /**
     * 获取指定级别的消息数量
     *
     * @param string|null $level 消息级别，null 返回总数
     * @return int
     */
    public static function count(?string $level = null): int
    {
        self::ensureSessionStarted();

        if (!isset($_SESSION[self::SESSION_KEY])) {
            return 0;
        }

        if ($level === null) {
            return array_sum(array_map('count', $_SESSION[self::SESSION_KEY]));
        }

        return count($_SESSION[self::SESSION_KEY][$level] ?? []);
    }

    /**
     * 将消息格式化为简单字符串数组
     *
     * @param array $messages 原始消息数组
     * @return array 格式化后的消息数组
     */
    private static function formatMessages(array $messages): array
    {
        $formatted = [];

        foreach ($messages as $level => $items) {
            $formatted[$level] = array_map(
                fn ($item) => is_array($item) ? $item['message'] : $item,
                $items
            );
        }

        return $formatted;
    }

    /**
     * 处理需要保持的消息
     *
     * @return void
     */
    private static function ageFlashData(): void
    {
        if (!isset($_SESSION[self::SESSION_KEEP_KEY])) {
            return;
        }

        $keep = $_SESSION[self::SESSION_KEEP_KEY];
        unset($_SESSION[self::SESSION_KEEP_KEY]);

        // 如果是 true，保持所有消息
        if ($keep === true) {
            return;
        }

        // 如果是特定级别，只保持该级别
        if (is_string($keep) && isset($_SESSION[self::SESSION_KEY])) {
            $messages = $_SESSION[self::SESSION_KEY];
            $_SESSION[self::SESSION_KEY] = [$keep => $messages[$keep] ?? []];
        }
    }

    /**
     * 确保会话已启动
     *
     * @return void
     */
    private static function ensureSessionStarted(): void
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
    }
}
